package com.syncomponents.remoting;

import org.perf4j.aop.Profiled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import com.sunkey.log.audit.annotation.Audit;
import com.syncomponents.core.RequestException;
import com.syncomponents.remoting.IMessage;

/**
 * 客户端调用重试机制，默认得试三次，1000与次数做偏移做间隔时间
 * 
 * @author sunkey
 * @date Mar 31, 2014
 */
public abstract class AbstractClient<T extends IMessage> implements IClient<T>, InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractClient.class);

    private String beanName;

    private int maxRetry = 2;
    private int interval = 1000;

    /**
     * 失败后有限重试请求, （后期修改掉, http组件通过配置本身就支持重试)
     */
    @Audit(action = "excute")
    @Profiled(logger = "com.fpx.ce.syn.core.client.AbstractClient")
    public Object excute(T messageRequest) {
        int retry = 1;
        int sleep = interval;

        while (true) {
            try {
                return doExcute(messageRequest);
            } catch (Exception e) {
                if (retry > maxRetry) {
                    throw new RequestException(e.getMessage(), e);
                }

                if (retry > 1) {
                    sleep = interval << retry;
                }

                LOGGER.info("request failure, retry to call client for the {} time after {} ms, messageRequest is {}",
                        retry, new Object[] { String.valueOf(sleep), messageRequest.getClass().getName() });

                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException e1) {
                    // do nothing
                }
                retry++;
                continue;
            }

        }
    }

    /**
     * 初始化，注册客户端
     */
    @Override
    public void afterPropertiesSet() throws Exception {
    }

    public int getMaxRetry() {
        return maxRetry;
    }

    public void setMaxRetry(int maxRetry) {
        this.maxRetry = maxRetry;
    }

    public int getInterval() {
        return interval;
    }

    public void setInterval(int interval) {
        this.interval = interval;
    }

    public abstract Object doExcute(T messageRequest);

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

}
